#include "OgreWidget.h"

#include <iostream>
using namespace std;

//#include "imdebug.h"

OgreWidget::OgreWidget( QWidget *parent ):
	QGLWidget( parent ),
	mOgreWindow(NULL),
	zoomFactor(1.)
{
	this -> setFocusPolicy(Qt::StrongFocus) ;
}

OgreWidget::~OgreWidget()
{
	mOgreRoot->shutdown();
	delete mOgreRoot;
	destroy();
}


void OgreWidget::init( std::string plugins_file,
		 std::string ogre_cfg_file,
		 std::string ogre_log )
{
	// create the main ogre object
	mOgreRoot = new Ogre::Root( plugins_file, ogre_cfg_file, ogre_log );

	// setup a renderer
	Ogre::RenderSystemList renderers = mOgreRoot->getAvailableRenderers();
	assert( !renderers.empty() ); // we need at least one renderer to do anything useful

	Ogre::RenderSystem *renderSystem;
	renderSystem = chooseRenderer( &renderers );

	assert( renderSystem ); // user might pass back a null renderer, which would be bad!

	mOgreRoot->setRenderSystem( renderSystem );
	QString dimensions = QString( "%1x%2" )
    					.arg(this->width())
    					.arg(this->height());

	renderSystem->setConfigOption( "Video Mode", dimensions.toStdString() );

	// initialize without creating window
	mOgreRoot->getRenderSystem()->setConfigOption( "Full Screen", "No" );
	mOgreRoot->saveConfig();
	mOgreRoot->initialise(false); // don't create a window

}

void OgreWidget::createOgreWindow()
{
  //== Creating and Acquiring Ogre Window ==//

// linux handle
/*
	// Get the parameters of the window QT created 
	QX11Info info = x11Info();
	Ogre::String winHandle;
	winHandle  = Ogre::StringConverter::toString((unsigned long)(info.display()));
	winHandle += ":";
	winHandle += Ogre::StringConverter::toString((unsigned int)(info.screen()));
	winHandle += ":";
	winHandle += Ogre::StringConverter::toString((unsigned long)(this->parentWidget()->winId()));
*/

	//windows handle

	Ogre::NameValuePairList params;
	params["externalGLControl"] = Ogre::String("True");
	params["externalWindowHandle"] = Ogre::StringConverter::toString((unsigned int)(winId()));

	mOgreWindow = mOgreRoot->createRenderWindow( "QOgreWidget_RenderWindow",
						   this->width(),
						   this->height(),
						   false,
						   &params );
	mOgreWindow->setActive(true);

	setAttribute( Qt::WA_PaintOnScreen, true );
	setAttribute( Qt::WA_NoBackground );

}


void OgreWidget::paintGL()
{
	if(mOgreWindow){
		assert( mOgreWindow );
		mOgreRoot->renderOneFrame();

	}
}


void OgreWidget::resizeGL(int w, int h)
{
  //assert( mOgreWindow );
  if(mOgreWindow){
	mOgreWindow->windowMovedOrResized();
	mCamera->setAspectRatio(Ogre::Real(w) / Ogre::Real(h));
	mViewport->update();
	updateGL();
  }
}


Ogre::RenderSystem* OgreWidget::chooseRenderer( Ogre::RenderSystemList *renderers )
{
	// It would probably be wise to do something more friendly 
	// that just use the first available renderer
	return *renderers->begin();
}

void OgreWidget::initializeGL(){

	// init Ogre
	init( "plugins.cfg", "ogre.cfg", "ogre.log" );

	// create render window
	createOgreWindow();

	// setup the resource paths: materials, meshes, ...
	setupResources();

	// create the scene manager, main camera, and viewport.
	createSceneManagerAndCamera();

	// create the scene
	createScene();
	
}

void OgreWidget::setupResources(void) {
	// Load resource paths from config file
	Ogre::ConfigFile config;
	config.load("resources.cfg");
	
	// Go through all sections & settings in the file
	Ogre::ConfigFile::SectionIterator it = config.getSectionIterator();
	
	Ogre::String secName, typeName, archName;
	while (it.hasMoreElements()) {
		secName = it.peekNextKey();
		Ogre::ConfigFile::SettingsMultiMap *settings = it.getNext();
		Ogre::ConfigFile::SettingsMultiMap::iterator i;
		
		for (i = settings->begin(); i != settings->end(); ++i) {
			typeName = i->first;
			archName = i->second;
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
		}
    }
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

void OgreWidget::createSceneManagerAndCamera(){

	//== Ogre Initialization ==//
	mSceneMgr = mOgreRoot->createSceneManager(Ogre::ST_GENERIC );
	mSceneMgr->setAmbientLight(Ogre::ColourValue(1.,1.,1.));

	mCamera = mSceneMgr->createCamera( "QOgreWidget_Cam" );
	mCamera->setNearClipDistance(1);
	mCamera->setAspectRatio(Ogre::Real(width()) / Ogre::Real(height()));
	mCamera->setFixedYawAxis(false);
	

	mViewport = mOgreWindow->addViewport( mCamera );
	mViewport->setBackgroundColour( Ogre::ColourValue( 0,0,0 ) );
	mViewport->setClearEveryFrame(true);
	mCamera->setAspectRatio(Ogre::Real(width()) / Ogre::Real(height()));

	//set initial camera position
	setUpAxis();
	setInitialCameraPosition();
}



void
OgreWidget::createScene(){

	mainNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

	lightNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
 	Ogre::Light* l = mSceneMgr->createLight("MainLight");
	lightNode->attachObject(l);
	lightNode->setPosition(Ogre::Vector3(20.,10.,40.));

	Ogre::Entity *mainEntity = mSceneMgr->createEntity("mainEntity","ogrehead.mesh");
	mainNode->attachObject(mainEntity);

	//paint axis
	paintAxis();

}

void 
OgreWidget::mousePressEvent(QMouseEvent *e) {
	mousePressed = true;
	mousePressPos = e->pos();
	buttonPressed = e->button();

	mousePos = (-1,-1);
}

void 
OgreWidget::mouseReleaseEvent(QMouseEvent *) {
	mousePressed = false;
}

void 
OgreWidget::mouseMoveEvent(QMouseEvent *e) {

	Ogre::Radian mRotX, mRotY;

	if ((mousePressed)){
		QPoint curPos = e->pos();
			
		Ogre::Vector2 mouseCurPos((Ogre::Real)curPos.x(),(Ogre::Real)curPos.y());

		if(mousePos[0] != -1){
	
						
			Ogre::Vector2 mouseRelPos(mouseCurPos-mousePos);

			Ogre::Vector3 mtv;
	
			float pan_accelerator = 1.;

			switch(buttonPressed){
				case Qt::RightButton:
	
					cameraZoom += mouseRelPos[1]*3*zoomFactor;
					updateCamera();

					break;

				case Qt::LeftButton:
					mouseRotation += mouseRelPos*0.5;
					updateCamera();

					break;
				case Qt::MidButton:
	
					mouseRotation += mouseRelPos*0.5;
					mRotX = Ogre::Degree(-mouseRelPos[0]* 0.1);
					mRotY = Ogre::Degree(-mouseRelPos[1]* 0.1);
					mCamera->yaw(mRotX);
					mCamera->pitch(mRotY);

					break;
	
	
			}

		}

		mousePos = mouseCurPos;
	}

	updateGL();

}

void 
OgreWidget::wheelEvent(QWheelEvent *e){

	
	float wheel_desaccelerator = (float)0.050;
	cameraZoom += -e->delta() * wheel_desaccelerator*zoomFactor;

	updateCamera();
	updateGL();
}

void 
OgreWidget::updateCamera(){

	// Quats to work with
	Ogre::Quaternion cameraRotationAboutWorldY, cameraRotationAboutWorldX, result; 

	// Set up the quaternions using the mouse input
	cameraRotationAboutWorldY.FromAngleAxis(Ogre::Degree(mouseRotation[0]),upAxis);
	cameraRotationAboutWorldX.FromAngleAxis(Ogre::Degree(-mouseRotation[1]),Ogre::Vector3::UNIT_X);

	// Get the result
	result = cameraRotationAboutWorldY * cameraRotationAboutWorldX;

	// Now play with the camera
	mCamera->setPosition(Ogre::Vector3::ZERO);
	mCamera->setOrientation(result);
	mCamera->moveRelative(Ogre::Vector3(0,cameraZoom,0));
	mCamera->lookAt(Ogre::Vector3::ZERO);
}

void 
OgreWidget::setInitialCameraPosition(){

	mouseRotation[0] = 360;
	mouseRotation[1] = -71.5;
	cameraZoom = 138;

	updateCamera();

}

void 
OgreWidget::setUpAxis(){
	upAxis = Ogre::Vector3::UNIT_Y;
}

void 
OgreWidget::keyPressEvent ( QKeyEvent * event ){

	switch(event->key()){
		case Qt::Key_M:{
				lightNode->translate(Ogre::Vector3(0,10,0));
		   }
			break;
		case Qt::Key_N:{
				lightNode->translate(Ogre::Vector3(0,-10,0));
			}
			break;
		case Qt::Key_Right:{
				lightNode->translate(Ogre::Vector3(10,0,0));
			}
			break;
		case Qt::Key_Left:{
				lightNode->translate(Ogre::Vector3(-10,0,0));
			}
			break;

		case Qt::Key_Down:{
				lightNode->translate(Ogre::Vector3(0,0,10));
			}
			break;
		case Qt::Key_Up:{
				lightNode->translate(Ogre::Vector3(0,0,-10));
			}
			break;

		case Qt::Key_W:{
			mCamera->setPolygonMode(Ogre::PM_WIREFRAME);
			}
			break;

		case Qt::Key_Q:{
			mCamera->setPolygonMode(Ogre::PM_SOLID);
			}
			break;

		case Qt::Key_P:{
				cout << "Camera information:" << endl; // to gather new initial camera positions
				cout << "mouseRotation: " << mouseRotation << endl;
				cout << "camerazoom: " << cameraZoom << endl;
			}
			break;
		case Qt::Key_O:{
			}
			break;

	}
	updateGL();
}


void
OgreWidget::paintAxis(){
	

	Ogre::ManualObject *XAxis = mSceneMgr->createManualObject("Xaxis"); 
	Ogre::ManualObject *YAxis = mSceneMgr->createManualObject("Yaxis"); 
	Ogre::ManualObject *ZAxis = mSceneMgr->createManualObject("Zaxis"); 

	mainNode->attachObject(XAxis);
	mainNode->attachObject(YAxis);
	mainNode->attachObject(ZAxis);

	XAxis -> begin("red", Ogre::RenderOperation::OT_LINE_LIST);	
		XAxis -> position(Ogre::Vector3(0.,0.,0.));
		XAxis -> position(Ogre::Vector3(500.,0.,0.));
	XAxis->end();

	YAxis -> begin("green", Ogre::RenderOperation::OT_LINE_LIST);	
		YAxis -> position(Ogre::Vector3(0.,0.,0.));
		YAxis -> position(Ogre::Vector3(0.,500.,0.));
	YAxis->end();

	ZAxis -> begin("blue", Ogre::RenderOperation::OT_LINE_LIST);	
		ZAxis -> position(Ogre::Vector3(0.,0.,0.));
		ZAxis -> position(Ogre::Vector3(0.,0.,500.));
	ZAxis->end();

}

